import { acceptHMRUpdate, defineStore } from "pinia";
import { nodejsMirrorList } from "@/pages/tools/nodejs/mirror/nodejs-mirror";
import { getLocalJson, setLocalJson } from "@/framework/util/storage";
import { computed, ref, shallowRef, watch } from "vue";
import { NpmrcFile } from "@/pages/tools/nodejs/npmrc-file";
import type { GenericToolItemVO } from "@/pages/tools/common/vos";
import { deleteFile, existsFile, readDir, unlinkFile } from "@/framework/util/fs";
import path from "path-browserify-esm";
import semver from "semver";
import { exposeGlobal } from "@/framework/util/dev-helper";
import { mklinkDir, spawn } from "@/framework/util/cp";
import { openFileViaEditor } from "@/framework/util/shell";
import type { NodejsMirror, NodeJsMirrorSource } from "@/pages/tools/nodejs/mirror/types";

export interface NodejsConfig {
  // 下载镜像源
  downloadMirror: NodeJsMirrorSource;
  // 本地保存路径
  localSavePath: string;
  // 当前系统使用版本路径
  currentVersionPath: string;
  // 长期支持的版本
  ltsVersions: string;
}

export interface NpmConfig {
  registry: string;
  electronMirror: string;
  electronBuilderBinariesMirror: string;
  pythonMirror: string;
}

const defaultNodeConfig: NodejsConfig = {
  downloadMirror: "npmmirror",
  localSavePath: "C:\\Dev\\Denv\\nodejs",
  currentVersionPath: "C:\\Dev\\Denv\\nodejs_current",
  ltsVersions: "20,18,16,14,12,10,8",
};

const defaultNpmConfig: NpmConfig = {
  registry: "https://registry.npmmirror.com/",
  electronMirror: "https://npmmirror.com/mirrors/electron/",
  electronBuilderBinariesMirror: "https://npmmirror.com/mirrors/electron-builder-binaries/",
  pythonMirror: "https://npmmirror.com/mirrors/python/",
};

export const useNodejsStore = defineStore("nodejs", () => {
  const nodeConfig = ref<NodejsConfig>(getLocalJson<NodejsConfig>("nodejs.config") || { ...defaultNodeConfig });
  const currentMirror = computed<NodejsMirror>(() => nodejsMirrorList.find(it => it.name === nodeConfig.value.downloadMirror)!);

  const setNodeConfig = (config: NodejsConfig) => {
    nodeConfig.value = config;
    setLocalJson("nodejs.config", config);
  };

  const npmConfigLoading = ref(false);
  const npmConfig = ref<NpmConfig>({ ...defaultNpmConfig });
  const npmrcFile = new NpmrcFile();

  const setNpmConfig = (config: NpmConfig): void => void (npmConfig.value = config);

  const loadNpmConfig = async () => {
    try {
      npmConfigLoading.value = true;
      await npmrcFile.load();
      npmConfig.value = {
        registry: npmrcFile.get("registry") || defaultNpmConfig.registry,
        electronMirror: npmrcFile.get("electron_mirror") || defaultNpmConfig.electronMirror,
        electronBuilderBinariesMirror: npmrcFile.get("electron_builder_binaries_mirror") || defaultNpmConfig.electronBuilderBinariesMirror,
        pythonMirror: npmrcFile.get("python_mirror") || defaultNpmConfig.pythonMirror,
      };
    } catch (e: any) {
      console.error("load npm config error", e);
    } finally {
      npmConfigLoading.value = false;
    }
  };

  const saveNpmConfig = async () => {
    npmrcFile.set("registry", npmConfig.value.registry);
    npmrcFile.set("electron_mirror", npmConfig.value.electronMirror);
    npmrcFile.set("electron_builder_binaries_mirror", npmConfig.value.electronBuilderBinariesMirror);
    npmrcFile.set("python_mirror", npmConfig.value.pythonMirror);
    await npmrcFile.save();
  };

  const openNpmrcConfigFile = () => openFileViaEditor(npmrcFile.filePath);

  const installedVersionList = shallowRef<GenericToolItemVO[]>([]);
  const versionsMap = computed<Record<string, GenericToolItemVO>>(() =>
    installedVersionList.value.reduce((acc, it) => ({ ...acc, [it.version]: it }), {} as Record<string, GenericToolItemVO>),
  );
  const currentVersion = shallowRef<GenericToolItemVO | null>(null);

  const getInstalledVersionListFormLocal = async () => {
    const localSavePath = nodeConfig.value.localSavePath;
    const list = await readDir(localSavePath);
    const versionList: GenericToolItemVO[] = [];
    for (const it of list) {
      if (it.startsWith("v")) {
        const versionDir = path.resolve(localSavePath, it);
        let bin = path.resolve(versionDir, "node.exe");
        if (!(await existsFile(bin))) {
          bin = path.resolve(versionDir, "node64.exe");
        }
        if (await existsFile(bin)) {
          const version = it.replace("v", "");
          versionList.push({ version, installPath: versionDir, bin });
        }
      }
    }
    versionList.sort((a, b) => semver.compare(b.version, a.version));
    return versionList;
  };

  const refreshInstalledVersionList = async () => (installedVersionList.value = await getInstalledVersionListFormLocal());

  const getCurrentVersionFromLocal = async (): Promise<GenericToolItemVO | null> => {
    const currentVersionPath = nodeConfig.value.currentVersionPath;
    const bin = path.join(currentVersionPath, "node.exe");
    if (await existsFile(bin)) {
      const versionOutput = await spawn(bin, ["-v"]);
      const version = versionOutput.trim().replace("v", "");
      if (version) {
        return { version, installPath: currentVersionPath, bin };
      }
    }
    return null;
  };

  watch(
    () => nodeConfig.value.localSavePath,
    async () => (installedVersionList.value = await getInstalledVersionListFormLocal()),
    { immediate: true },
  );
  watch(
    () => nodeConfig.value.currentVersionPath,
    async () => (currentVersion.value = await getCurrentVersionFromLocal()),
    { immediate: true },
  );

  const uninstallVersion = async (record: GenericToolItemVO) => {
    const versionDir = record.installPath;
    await deleteFile(versionDir, { recursive: true });
    await refreshInstalledVersionList();
    currentVersion.value = await getCurrentVersionFromLocal();
  };

  const setDefaultVersion = async (record: GenericToolItemVO) => {
    const currentVersionPath = nodeConfig.value.currentVersionPath;
    if (await existsFile(currentVersionPath)) {
      await unlinkFile(currentVersionPath);
    }
    await mklinkDir(record.installPath, currentVersionPath, "nodejs-set-current-version");
    await refreshInstalledVersionList();
    currentVersion.value = await getCurrentVersionFromLocal();
  };

  return {
    nodeConfig: nodeConfig,
    currentMirror: currentMirror,
    setNodeConfig: setNodeConfig,

    npmConfigLoading: npmConfigLoading,
    npmConfig: npmConfig,
    npmrcFile: npmrcFile,
    setNpmConfig: setNpmConfig,
    loadNpmConfig: loadNpmConfig,
    saveNpmConfig: saveNpmConfig,
    openNpmrcConfigFile: openNpmrcConfigFile,

    installedVersionList: installedVersionList,
    versionsMap: versionsMap,
    currentVersion: currentVersion,
    getInstalledVersionListFromLocal: getInstalledVersionListFormLocal,
    refreshInstalledVersionList: refreshInstalledVersionList,

    uninstallVersion: uninstallVersion,
    setDefaultVersion: setDefaultVersion,
  };
});

exposeGlobal("__nodejs_store__", () => useNodejsStore());
if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useNodejsStore, import.meta.hot));
}
